package com.itheima.cacheable;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.annotation.Resource;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

@Aspect
@Component
public class CacheAspect {

    @Resource
    private JedisPool jedisPool;




    @Around("@annotation(cache)")
    public Object cached(ProceedingJoinPoint pjp, Cacheable cache) {
        Jedis jedis = jedisPool.getResource();
        //获取调用对象应该存在redis数据库中的key（方法全类名+方法名+参数）
        String key = this.getCacheKey(pjp);
        //根据key获取redis中的值
        String value = jedis.get(key);
        //判断获取的value不为空
        System.out.println(!StringUtils.isEmpty(value));
        if(!StringUtils.isEmpty(value)) {
            return this.isJSONArray(value, pjp);
        }
        //获取的value值为空，自定义一个空值
        Object  obj = null;
        try {
            //执行目标方法
            obj = pjp.proceed();
            //目标方法返回值为Null,直接返回结果
            if(obj == null) {
                return obj;
            }
            //目标方法返回值不为null，将目标方法的返回值转换成json字符串
            value = JSON.toJSONString(obj);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        if(cache.expire() <= 0) {
            //获取注解中设置的超时时长为永久有效，将键值对数据永久存入redis数据库
            jedis.set(key, value);
        } else {
            //获取注解中设置超时时长为cache.expire() ,将键值对数据永久存入redis数据库，存活时间为cache.expire()秒
            jedis.setex(key,cache.expire(),value);
        }
        if(obj instanceof List) {
            //判断目标方法执行结果返回值为list集合
            if(((List)obj).size() > 0) {
                //返回值集合长度大于0，获取集合泛型类型
                Class<?> entityClazz = ((List)obj).toArray()[0].getClass();
                //
                return JSONArray.parseArray(value, entityClazz);
            }
            //返回值集合长度为0，直接转换
            return JSONArray.parseArray(value);
        }

        jedis.close();
        //返回值为非集合类型，
        return this.isJSONArray(value, pjp);
    }

    /**
     * 根据目标方法对象和value值将value值转换为对象
     * @param value json字符串
     * @param pjp   目标方法对象
     * @return  目标方法原返回对象
     */
    private Object isJSONArray(String value, ProceedingJoinPoint pjp) {
        //将value解析为对象
        Object obj = JSONObject.parse(value);
        //获取方法签名
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        //获取方法返回值类型
        Type type = signature.getMethod().getGenericReturnType();
        try {
            //判断redis中获取的值是否是JSONArray类型
            if (obj instanceof JSONArray) {
                //ParameterizedType 参数化类型

                if (ParameterizedType.class.isAssignableFrom(type.getClass())) {
                    //getActualTypeArguments()  获取参数化数组，包含的是调用者的泛型里面的参数的类型
                    //将type强转为参数化类型，并获取参数类型数组
                    Type[] var7 = ((ParameterizedType) type).getActualTypeArguments();
                    int var8 = var7.length;
                    byte var9 = 0;
                    if (var9 < var8) {
                        //参数化类型素组长度大于0
                        Type t1 = var7[var9];
                        //将JSONArray类型转化为原本类型返回
                        return JSONArray.parseArray(value, (Class) t1);
                    }
                }
                return null;
            } else {
                //非JSONArray类型，直接解析
                //value 值
                //将json字符串转换为原本返回的对象类型
                return JSON.parseObject(value, Class.forName(type.getTypeName()));
            }
        } catch (ClassNotFoundException | JSONException var11) {
            return null;
        }
    }

    /**
     * 获取全类名+方法名+参数作为key
     * @param pjp   目标方法对象
     * @return  方法签名+参数信息
     */
    private String getCacheKey(ProceedingJoinPoint pjp) {
        StringBuilder buf = new StringBuilder();
        Signature signature = pjp.getSignature();
        String declaringTypeName = signature.getDeclaringTypeName();
        String name = signature.getName();
        buf.append(declaringTypeName).append(".").append(name);
        Object[] args = pjp.getArgs();
        Annotation[][] pas = ((MethodSignature)signature).getMethod().getParameterAnnotations();
        int length = pas.length;
        for(int i = 0; i < length; ++i) {
            Annotation[] var1 = pas[i];
            int var2 = var1.length;
            for(int var3 = 0; var3 < var2; ++var3) {
                Annotation an = var1[var3];
                if(an instanceof CacheKey) {
                    buf.append("|").append(((CacheKey)an).key()).append("=").append(args[i].toString());
                    break;
                }
            }
        }
        return buf.toString();
    }
}